package com.im.service.group.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;;
import com.im.codec.pack.group.CreateGroupPack;
import com.im.codec.pack.group.DestroyGroupPack;
import com.im.codec.pack.group.UpdateGroupInfoPack;
import com.im.service.config.AppConfig;
import com.im.common.constant.Constants;
import com.im.common.enums.exception.GroupErrorCode;
import com.im.common.enums.GroupMemberRoleEnum;
import com.im.common.enums.GroupStatusEnum;
import com.im.common.enums.GroupTypeEnum;
import com.im.common.enums.command.GroupEventCommand;
import com.im.common.exception.BusinessException;
import com.im.common.model.ClientInfo;
import com.im.common.model.SyncReq;
import com.im.common.model.SyncResp;
import com.im.service.group.entity.ImGroupEntity;
import com.im.service.group.mapper.ImGroupMapper;
import com.im.service.group.model.callback.DestroyGroupCallbackDto;
import com.im.service.group.model.req.*;
import com.im.service.group.model.resp.GetGroupResp;
import com.im.service.group.model.resp.GetJoinedGroupResp;
import com.im.service.group.model.resp.GetRoleInGroupResp;
import com.im.service.group.service.ImGroupMemberService;
import com.im.service.group.service.ImGroupService;
import com.im.service.utils.CallbackService;
import com.im.service.utils.GroupMessageProducer;
import com.im.service.utils.RedisSeq;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;

@Service
public class ImGroupServiceImpl implements ImGroupService {

    @Autowired
    private ImGroupMapper imGroupDataMapper;
    @Autowired
    private ImGroupMemberService groupMemberService;
    @Autowired
    private AppConfig appConfig;
    @Autowired
    private CallbackService callbackService;
    @Autowired
    private GroupMessageProducer groupMessageProducer;
    @Autowired
    private RedisSeq redisSeq;

    @Override
    public void importGroup(ImportGroupReq req) {
        //1.判断群id是否存在
        QueryWrapper<ImGroupEntity> query = new QueryWrapper<>();

        if (StringUtils.isEmpty(req.getGroupId())) {
            req.setGroupId(UUID.randomUUID().toString().replace("-", ""));
        } else {
            query.eq("group_id", req.getGroupId());
            query.eq("app_id", req.getAppId());
            Integer integer = imGroupDataMapper.selectCount(query);
            if (integer > 0) {
                throw new BusinessException(GroupErrorCode.GROUP_IS_EXIST);
            }
        }

        ImGroupEntity imGroupEntity = new ImGroupEntity();

        if (req.getGroupType() == GroupTypeEnum.PUBLIC.getCode() && StringUtils.isBlank(req.getOwnerId())) {
            throw new BusinessException(GroupErrorCode.PUBLIC_GROUP_MUST_HAVE_OWNER);
        }

        if (req.getCreateTime() == null) {
            imGroupEntity.setCreateTime(System.currentTimeMillis());
        }
        imGroupEntity.setStatus(GroupStatusEnum.NORMAL.getCode());
        BeanUtils.copyProperties(req, imGroupEntity);
        int insert = imGroupDataMapper.insert(imGroupEntity);

        if (insert != 1) {
            throw new BusinessException(GroupErrorCode.IMPORT_GROUP_ERROR);
        }
    }

    @Override
    @Transactional
    public void createGroup(CreateGroupReq req) {
        boolean isAdmin = false;
        if (!isAdmin) {
            req.setOwnerId(req.getOperator());
        }
        //1.判断群id是否存在
        QueryWrapper<ImGroupEntity> query = new QueryWrapper<>();

        if (StringUtils.isEmpty(req.getGroupId())) {
            req.setGroupId(UUID.randomUUID().toString().replace("-", ""));
        } else {
            query.eq("group_id", req.getGroupId());
            query.eq("app_id", req.getAppId());
            Integer integer = imGroupDataMapper.selectCount(query);
            if (integer > 0) {
                throw new BusinessException(GroupErrorCode.GROUP_IS_EXIST);
            }
        }

        if (req.getGroupType() == GroupTypeEnum.PUBLIC.getCode() && StringUtils.isBlank(req.getOwnerId())) {
            throw new BusinessException(GroupErrorCode.PUBLIC_GROUP_MUST_HAVE_OWNER);
        }

        ImGroupEntity imGroupEntity = new ImGroupEntity();
        long seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.Group);
        imGroupEntity.setSequence(seq);
        imGroupEntity.setCreateTime(System.currentTimeMillis());
        imGroupEntity.setStatus(GroupStatusEnum.NORMAL.getCode());
        BeanUtils.copyProperties(req, imGroupEntity);
        imGroupDataMapper.insert(imGroupEntity);

        GroupMemberDto groupMemberDto = new GroupMemberDto();
        groupMemberDto.setMemberId(req.getOwnerId());
        groupMemberDto.setRole(GroupMemberRoleEnum.OWNER.getCode());
        groupMemberDto.setJoinTime(System.currentTimeMillis());
        groupMemberService.addGroupMember(req.getGroupId(), req.getAppId(), groupMemberDto);

        //插入群成员
        for (GroupMemberDto dto : req.getMember()) {
            groupMemberService.addGroupMember(req.getGroupId(), req.getAppId(), dto);
        }

        if (appConfig.isCreateGroupAfterCallback()) {
            callbackService.callback(req.getAppId(), Constants.CallbackCommand.CreateGroupAfter, JSONObject.toJSONString(imGroupEntity));
        }

        CreateGroupPack createGroupPack = new CreateGroupPack();
        BeanUtils.copyProperties(imGroupEntity, createGroupPack);
        groupMessageProducer.producer(req.getOperator(), GroupEventCommand.CREATED_GROUP, createGroupPack, new ClientInfo(req.getAppId(), req.getClientType(), req.getImei()));
    }

    /**
     * 修改群基础信息，如果是后台管理员调用，则不检查权限，如果不是则检查权限，如果是私有群（微信群）任何人都可以修改资料，公开群只有管理员可以修改
     * 如果是群主或者管理员可以修改其他信息。
     */
    @Override
    @Transactional
    public void updateBaseGroupInfo(UpdateGroupReq req) {
        // 查询群组信息
        QueryWrapper<ImGroupEntity> query = new QueryWrapper<>();
        query.eq("group_id", req.getGroupId());
        query.eq("app_id", req.getAppId());
        ImGroupEntity imGroupEntity = imGroupDataMapper.selectOne(query);

        // 判断群组是否存在
        if (imGroupEntity == null) {
            throw new BusinessException(GroupErrorCode.GROUP_IS_EXIST);
        }

        // 判断群组状态
        if (imGroupEntity.getStatus() == GroupStatusEnum.DESTROY.getCode()) {
            throw new BusinessException(GroupErrorCode.GROUP_IS_DESTROY);
        }

        boolean isAdmin = false;

        // 判断操作者是否为管理员
        if (!isAdmin) {
            GetRoleInGroupResp role = groupMemberService.getRoleInGroupOne(req.getGroupId(), req.getOperator(), req.getAppId());
            Integer roleInfo = role.getRole();
            boolean isManager = roleInfo == GroupMemberRoleEnum.MAMAGER.getCode() || roleInfo == GroupMemberRoleEnum.OWNER.getCode();

            // 非管理员无法更新公开群的信息
            if (!isManager && GroupTypeEnum.PUBLIC.getCode() == imGroupEntity.getGroupType()) {
                throw new BusinessException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE);
            }
        }

        // 更新群组信息
        ImGroupEntity update = new ImGroupEntity();
        long seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.Group);
        BeanUtils.copyProperties(req, update);
        update.setUpdateTime(System.currentTimeMillis());
        update.setSequence(seq);
        int row = imGroupDataMapper.update(update, query);

        // 判断更新是否成功
        if (row != 1) {
            throw new BusinessException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE);
        }

        // 调用更新群组后的回调
        if (appConfig.isModifyGroupAfterCallback()) {
            callbackService.callback(req.getAppId(), Constants.CallbackCommand.UpdateGroupAfter, JSONObject.toJSONString(imGroupDataMapper.selectOne(query)));
        }

        // 发送更新群组信息的消息
        UpdateGroupInfoPack pack = new UpdateGroupInfoPack();
        BeanUtils.copyProperties(req, pack);
        groupMessageProducer.producer(req.getOperator(), GroupEventCommand.UPDATED_GROUP, pack, new ClientInfo(req.getAppId(), req.getClientType(), req.getImei()));
    }

    /**
     * 获取用户加入的群组
     */
    @Override
    public GetJoinedGroupResp getJoinedGroup(GetJoinedGroupReq req) {
        GetJoinedGroupResp resp = new GetJoinedGroupResp();
        Collection<String> memberJoinedGroup = groupMemberService.getMemberJoinedGroup(req);
        if (CollectionUtil.isEmpty(memberJoinedGroup)) {
            resp.setTotalCount(0);
            resp.setGroupList(new ArrayList<>());
            return resp;
        }
        QueryWrapper<ImGroupEntity> query = new QueryWrapper<>();
        query.eq("app_id", req.getAppId());
        query.in("group_id", memberJoinedGroup);

        if (CollectionUtils.isNotEmpty(req.getGroupType())) {
            query.in("group_type", req.getGroupType());
        }

        List<ImGroupEntity> groupList = imGroupDataMapper.selectList(query);
        resp.setGroupList(groupList);
        if (req.getLimit() == null) {
            resp.setTotalCount(groupList.size());
        } else {
            resp.setTotalCount(imGroupDataMapper.selectCount(query));
        }
        return resp;
    }

    @Override
    @Transactional
    public void destroyGroup(DestroyGroupReq req) {
        boolean isAdmin = false;
        // 查询群组信息
        QueryWrapper<ImGroupEntity> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.eq("group_id", req.getGroupId());
        objectQueryWrapper.eq("app_id", req.getAppId());
        ImGroupEntity imGroupEntity = imGroupDataMapper.selectOne(objectQueryWrapper);
        // 判断群组是否存在
        if (imGroupEntity == null) {
            throw new BusinessException(GroupErrorCode.PRIVATE_GROUP_CAN_NOT_DESTORY);
        }
        // 判断群组状态
        if (imGroupEntity.getStatus() == GroupStatusEnum.DESTROY.getCode()) {
            throw new BusinessException(GroupErrorCode.GROUP_IS_DESTROY);
        }
        // 判断操作者是否为管理员
        if (!isAdmin) {
            // 非管理员无法解散公开群和非自己创建的私有群
            if (imGroupEntity.getGroupType() == GroupTypeEnum.PUBLIC.getCode()
                    || !imGroupEntity.getOwnerId().equals(req.getOperator())) {
                throw new BusinessException(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE);
            }
        }
        // 更新群组信息
        ImGroupEntity entity = new ImGroupEntity();
        long seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.Group);
        entity.setStatus(GroupStatusEnum.DESTROY.getCode());
        entity.setSequence(seq);
        int update = imGroupDataMapper.update(entity, objectQueryWrapper);
        // 判断更新是否成功
        if (update != 1) {
            throw new BusinessException(GroupErrorCode.UPDATE_GROUP_BASE_INFO_ERROR);
        }
        // 调用解散群组后的回调
        if (appConfig.isModifyGroupAfterCallback()) {
            DestroyGroupCallbackDto dto = new DestroyGroupCallbackDto();
            dto.setGroupId(req.getGroupId());
            callbackService.callback(req.getAppId(), Constants.CallbackCommand.DestoryGroupAfter, JSONObject.toJSONString(dto));
        }
        // 发送解散群组消息
        DestroyGroupPack pack = new DestroyGroupPack();
        pack.setSequence(seq);
        pack.setGroupId(req.getGroupId());
        groupMessageProducer.producer(req.getOperator(), GroupEventCommand.DESTROY_GROUP, pack, new ClientInfo(req.getAppId(), req.getClientType(), req.getImei()));
    }


    @Override
    @Transactional
    public void transferGroup(TransferGroupReq req) {
        // 获取操作者在群组中的角色
        GetRoleInGroupResp roleInGroupOne = groupMemberService.getRoleInGroupOne(req.getGroupId(), req.getOperator(), req.getAppId());

        // 判断操作者是否为群组的拥有者
        if (roleInGroupOne.getRole() != GroupMemberRoleEnum.OWNER.getCode()) {
            throw new BusinessException(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE);
        }

        // 获取转让目标用户在群组中的角色
        groupMemberService.getRoleInGroupOne(req.getGroupId(), req.getOwnerId(), req.getAppId());

        // 查询群组信息
        QueryWrapper<ImGroupEntity> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.eq("group_id", req.getGroupId());
        objectQueryWrapper.eq("app_id", req.getAppId());
        ImGroupEntity imGroupEntity = imGroupDataMapper.selectOne(objectQueryWrapper);

        // 判断群组状态
        if (imGroupEntity.getStatus() == GroupStatusEnum.DESTROY.getCode()) {
            throw new BusinessException(GroupErrorCode.GROUP_IS_DESTROY);
        }

        // 更新群组的拥有者
        ImGroupEntity updateGroup = new ImGroupEntity();
        updateGroup.setOwnerId(req.getOwnerId());
        UpdateWrapper<ImGroupEntity> updateGroupWrapper = new UpdateWrapper<>();
        updateGroupWrapper.eq("app_id", req.getAppId());
        updateGroupWrapper.eq("group_id", req.getGroupId());
        imGroupDataMapper.update(updateGroup, updateGroupWrapper);

        // 转让群组成员
        groupMemberService.transferGroupMember(req.getOwnerId(), req.getGroupId(), req.getAppId());
    }


    @Override
    public ImGroupEntity getGroup(String groupId, Integer appId) {
        QueryWrapper<ImGroupEntity> query = new QueryWrapper<>();
        query.eq("app_id", appId);
        query.eq("group_id", groupId);
        ImGroupEntity imGroupEntity = imGroupDataMapper.selectOne(query);

        if (imGroupEntity == null) {
            throw new BusinessException(GroupErrorCode.GROUP_IS_NOT_EXIST);
        }
        return imGroupEntity;
    }

    @Override
    public GetGroupResp getGroup(GetGroupReq req) {
        ImGroupEntity group = this.getGroup(req.getGroupId(), req.getAppId());
        GetGroupResp getGroupResp = new GetGroupResp();
        BeanUtils.copyProperties(group, getGroupResp);
        try {
            List<GroupMemberDto> groupMember = groupMemberService.getGroupMember(req.getGroupId(), req.getAppId());
            if (groupMember != null) {
                getGroupResp.setMemberList(groupMember);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return getGroupResp;
    }

    @Override
    public void muteGroup(MuteGroupReq req) {
        ImGroupEntity groupResp = getGroup(req.getGroupId(), req.getAppId());

        if (groupResp.getStatus() == GroupStatusEnum.DESTROY.getCode()) {
            throw new BusinessException(GroupErrorCode.GROUP_IS_DESTROY);
        }

        boolean isAdmin = false;

        if (!isAdmin) {
            //不是后台调用需要检查权限
            GetRoleInGroupResp role = groupMemberService.getRoleInGroupOne(req.getGroupId(), req.getOperator(), req.getAppId());
            Integer roleInfo = role.getRole();
            boolean isManager = roleInfo == GroupMemberRoleEnum.MAMAGER.getCode() || roleInfo == GroupMemberRoleEnum.OWNER.getCode();

            //公开群只能群主修改资料
            if (!isManager) {
                throw new BusinessException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE);
            }
        }

        ImGroupEntity update = new ImGroupEntity();
        update.setMute(req.getMute());

        UpdateWrapper<ImGroupEntity> wrapper = new UpdateWrapper<>();
        wrapper.eq("group_id", req.getGroupId());
        wrapper.eq("app_id", req.getAppId());
        imGroupDataMapper.update(update, wrapper);
    }

    @Override
    public SyncResp<ImGroupEntity> syncJoinedGroupList(SyncReq req) {
        if (req.getMaxLimit() > 100) {
            req.setMaxLimit(100);
        }
        SyncResp<ImGroupEntity> resp = new SyncResp<>();
        // 获取成员已加入的群组
        Collection<String> memberJoinedGroup = groupMemberService.syncMemberJoinedGroup(req.getOperator(), req.getAppId());
        if (memberJoinedGroup != null) {
            QueryWrapper<ImGroupEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("app_id", req.getAppId());
            queryWrapper.in("group_id", memberJoinedGroup);
            queryWrapper.gt("sequence", req.getLastSequence());
            queryWrapper.last(" limit " + req.getMaxLimit());
            queryWrapper.orderByAsc("sequence");

            // 查询满足条件的群组列表
            List<ImGroupEntity> list = imGroupDataMapper.selectList(queryWrapper);

            if (!CollectionUtils.isEmpty(list)) {
                ImGroupEntity maxSeqEntity = list.get(list.size() - 1);
                resp.setDataList(list);
                // 设置最大序列号
                Long maxSeq = imGroupDataMapper.getGroupMaxSeq(memberJoinedGroup, req.getAppId());
                resp.setMaxSequence(maxSeq);
                // 设置是否拉取完毕
                resp.setCompleted(maxSeqEntity.getSequence() >= maxSeq);
                return resp;
            }
        }
        resp.setCompleted(true);
        return resp;
    }

    @Override
    public Long getUserGroupMaxSeq(String userId, Integer appId) {
        Collection<String> memberJoinedGroup = groupMemberService.syncMemberJoinedGroup(userId, appId);
        if (CollectionUtil.isEmpty(memberJoinedGroup)) {
            throw new BusinessException(500, "");
        }
        Long maxSeq = imGroupDataMapper.getGroupMaxSeq(memberJoinedGroup, appId);
        return maxSeq;
    }

}
